# coding: utf-8

# Copyright (c) P SOFTHOUSE. All rights reserved.

import maya.cmds as cmds
import maya.mel as mel
import types

_objectStore = {}   # Melのエクスポート後にこのスクリプトをreload()すると消えてしまうので注意
_sep = '.'

def exportMelAsObj(pyobj, args=(), returnType=None, procName=None):
    '''
    create a MEL procedure from a python callable
    ※クラスメソッドやラムダをエクスポートできます。
      オブジェクトをバインドするので、元のPythonスクリプトをreloadしても変わりません。
      オブジェクトを置き換えたい場合は再度この関数で使用してください。

    :pyobj:
        any python callable

    :args: 
        a list of (type, name) pairs defining the arguments of the MEL procedure to create. should be
        compatible with the arguments of the passed python object.

    :procName:
        name to use for the MEL procedure.
        None の場合はpyobjとプリフィックスを使ってMELの名前を決定します。
        True の場合はpyobj.__name__をMELの名前とします。
        string の場合はその文字列をMELの名前とします。
    '''
    melParams = []
    pyParams = []
    melReturn = returnType if returnType else ''

    for type, name in args:
        melParams.append('{} ${}'.format(type, name))
        #if the arguement is a string we add ' before and after
        if type == 'string':
            pyParams.append(r"""'"+${}+"'""".format(name))
        else:
            pyParams.append(r'"+${}+"'.format(name))

    melProcName = None
    if isinstance(procName, bool) and bool(procName):
        melProcName = pyobj.__name__
    elif isinstance(procName, basestring) and len(procName) > 0:
        melProcName = procName
    else:
        procPrefix = 'PSOFTMelObj'
        melProcName = str(id(pyobj))

        try:
            if isinstance(pyobj, (types.FunctionType, types.BuiltinFunctionType)):
                melProcName = pyobj.__module__ + _sep + pyobj.__name__
                if pyobj.__name__ == '<lambda>':
                    melProcName += str(id(pyobj))
            elif isinstance(pyobj, types.MethodType):
                melProcName = pyobj.__module__ + _sep + pyobj.im_class.__name__ + _sep + pyobj.__name__
        except (AttributeError, TypeError):
            pass

        melProcName = _sep.join((procPrefix, melProcName))
        melProcName = melProcName.replace('<', '_').replace('>', '_').replace('-', '_')

    d = {}
    d['melProcName'] = melProcName
    d['melParams'] = ', '.join(melParams)
    d['pyParams'] = ', '.join(pyParams)
    d['melReturn'] = returnType if returnType else ''
    d['withReturn'] = 'return ' if returnType else '';
    d['thisModule'] = __name__

    contents = '''global proc {melReturn} {melProcName}({melParams}){{ {withReturn} python("import {thisModule};{thisModule}._objectStore['{melProcName}']({pyParams})"); }}''';
    mel.eval(contents.format(**d))
    _objectStore[melProcName] = pyobj

    return melProcName




def exportMel(pyobj, args=(), returnType=None, procName=None):
    '''
    create a MEL procedure from a python callable
    ※関数名でバインドします。
      元のPythonスクリプトをreloadすれば、新しい内容で実行されます。

    :pyobj:
        any python callable

    :args: 
        a list of (type, name) pairs defining the arguments of the MEL procedure to create. should be
        compatible with the arguments of the passed python object.

    :procName:
        name to use for the MEL procedure.
        None の場合はpyobjとプリフィックスを使ってMELの名前を決定します。
        True の場合はpyobj.__name__をMELの名前とします。
        string の場合はその文字列をMELの名前とします。
    '''


    if not isinstance(pyobj, (types.FunctionType, types.BuiltinFunctionType)):
        raise TypeError('FunctionType only')
    if pyobj.__name__ == '<lambda>':
        raise TypeError('lambda does not support')


    melParams = []
    pyParams = []
    melReturn = returnType if returnType else ''

    for type, name in args:
        melParams.append('{} ${}'.format(type, name))
        #if the arguement is a string we add ' before and after
        if type == 'string':
            pyParams.append(r"""'"+${}+"'""".format(name))
        else:
            pyParams.append(r'"+${}+"'.format(name))

    pyProcName = pyobj.__module__ + '.' + pyobj.__name__

    melProcName = None
    if isinstance(procName, bool) and bool(procName):
        melProcName = pyobj.__name__
        print melProcName
    elif isinstance(procName, basestring) and len(procName) > 0:
        melProcName = procName
    else:
        procPrefix = 'PSOFTMel'
        melProcName = _sep.join((procPrefix, pyProcName.replace('.', _sep)))

    d = {}
    d['melProcname'] = melProcName
    d['pyProcname'] = pyProcName
    d['melParams'] = ', '.join(melParams)
    d['pyParams'] = ', '.join(pyParams)
    d['melReturn'] = returnType if returnType else ''
    d['withReturn'] = 'return ' if returnType else '';
    d['pyModule'] = pyobj.__module__

    contents = '''global proc {melReturn} {melProcname}({melParams}){{ {withReturn} python("import {pyModule};{pyProcname}({pyParams})"); }}''';
    mel.eval(contents.format(**d))

    return melProcName



def getMayaVersion():
    version = cmds.about(f=True)
    return int(float(version[:4]))

